package net.sudot.chess.business.service;

import net.sudot.chess.exception.BusinessException;
import net.sudot.chess.base.Chess;
import net.sudot.chess.business.dao.BaseDao;
import net.sudot.chess.business.model.BaseEntity;
import net.sudot.commons.utils.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.NoResultException;
import java.util.HashSet;

/**
 * 基础Service
 *
 * @author tangjialin on 2018-03-16.
 */
public abstract class BaseService<E extends BaseEntity> extends Chess {
    /**
     * Bean拷贝时过滤的属性
     */
    private final static HashSet<String> $ignoreProperties = new HashSet<String>() {{
        add("id");
        add("version");
        add("deleted");
        add("createdDate");
        add("createdBy");
        add("lastModifiedDate");
        add("lastModifiedBy");
    }};

    @Autowired
    private BaseDao<E> baseDao;

    /**
     * 获取实体对象实例
     *
     * @param id 实体主键
     * @return 返回获取的实体对象实例
     */
    public E find(Long id) {
        return baseDao.find(id);
    }

    /**
     * 保存实体并设置实体必要公共属性
     *
     * @param e 期望添加的实体对象实例
     * @return 返回添加后的实体对象实例
     */
    @Transactional(rollbackFor = Exception.class)
    public E save(E e) {
        if (e.getId() != null) { throw new BusinessException("主键必须为空"); }
        e.setDeleted(false);
        return baseDao.persist(e);
    }

    @Transactional(rollbackFor = Exception.class)
    public E saveOrUpdate(E e) {
        if (e.getId() == null) {
            return save(e);
        } else {
            return update(e);
        }
    }

    /**
     * 保存(修改)实体
     *
     * @param e 期望保存的实体对象实例
     * @return 返回保存的实体对象实例
     */
    @Transactional(rollbackFor = Exception.class)
    public E update(E e) {
        return this.update(e, (HashSet<String>) null);
    }

    /**
     * 保存(修改)实体
     *
     * @param e                期望保存的实体对象实例
     * @param ignoreProperties 忽略的实体属性
     * @return 返回保存的实体对象实例
     */
    @Transactional(rollbackFor = Exception.class)
    public E update(E e, HashSet<String> ignoreProperties) {
        if (e.getId() == null) { throw new BusinessException("主键不能为空"); }
        E oldE = baseDao.find(e.getId());
        if (oldE == null) { throw new NoResultException(); }
        BeanUtils.copyProperties(e, oldE, BeanUtils.addIgnoreProperties($ignoreProperties, ignoreProperties));
        return baseDao.merge(oldE);
    }

    /**
     * 保存(修改)实体
     *
     * @param e                期望保存的实体对象实例
     * @param ignoreProperties 忽略的实体属性
     * @return 返回保存的实体对象实例
     */
    @Transactional(rollbackFor = Exception.class)
    public E update(E e, String... ignoreProperties) {
        return this.update(e, BeanUtils.addIgnoreProperties(null, ignoreProperties));
    }

    /**
     * 物理删除单实体
     *
     * @param e 期望删除的实体对象实例
     */
    @Transactional(rollbackFor = Exception.class)
    public void delete(E e) {
        baseDao.remove(e);
    }

    /**
     * 物理删除单实体
     *
     * @param id 期望删除的实体主键
     */
    @Transactional(rollbackFor = Exception.class)
    public void delete(Long id) {
        baseDao.remove(id);
    }

}
